/*-------------------------------------------------------------------------
 *
 * plpgsql.h		- PL/pgSQL 过程语言的定义
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/pl/plpgsql/src/plpgsql.h
 *
 *-------------------------------------------------------------------------
 */

#ifndef PLPGSQL_H
#define PLPGSQL_H

#include "access/xact.h"
#include "commands/event_trigger.h"
#include "commands/trigger.h"
#include "executor/spi.h"
#include "utils/expandedrecord.h"
#include "utils/typcache.h"


/**********************************************************************
 * 定义
 **********************************************************************/

/* 为翻译定义我们的文本域 */
#undef TEXTDOMAIN
#define TEXTDOMAIN PG_TEXTDOMAIN("plpgsql")

#undef _
#define _(x) dgettext(TEXTDOMAIN, x)

/*
 * 编译器命名空间项类型
 */
typedef enum PLpgSQL_nsitem_type
{
	PLPGSQL_NSTYPE_LABEL,		/* 块标签 */
	PLPGSQL_NSTYPE_VAR,			/* 标量变量 */
	PLPGSQL_NSTYPE_REC			/* 复合变量 */
} PLpgSQL_nsitem_type;

/*
 * 一个 PLPGSQL_NSTYPE_LABEL 栈条目必须是这些类型之一
 */
typedef enum PLpgSQL_label_type
{
	PLPGSQL_LABEL_BLOCK,		/* DECLARE/BEGIN 块 */
	PLPGSQL_LABEL_LOOP,			/* 循环结构 */
	PLPGSQL_LABEL_OTHER			/* 其他任何 */
} PLpgSQL_label_type;

/*
 * Datum 数组节点类型
 */
typedef enum PLpgSQL_datum_type
{
	PLPGSQL_DTYPE_VAR,
	PLPGSQL_DTYPE_ROW,
	PLPGSQL_DTYPE_REC,
	PLPGSQL_DTYPE_RECFIELD,
	PLPGSQL_DTYPE_PROMISE
} PLpgSQL_datum_type;

/*
 * DTYPE_PROMISE datums 有这些可能的承诺计算方式
 */
typedef enum PLpgSQL_promise_type
{
	PLPGSQL_PROMISE_NONE = 0,	/* 不是承诺，或承诺已满足 */
	PLPGSQL_PROMISE_TG_NAME,
	PLPGSQL_PROMISE_TG_WHEN,
	PLPGSQL_PROMISE_TG_LEVEL,
	PLPGSQL_PROMISE_TG_OP,
	PLPGSQL_PROMISE_TG_RELID,
	PLPGSQL_PROMISE_TG_TABLE_NAME,
	PLPGSQL_PROMISE_TG_TABLE_SCHEMA,
	PLPGSQL_PROMISE_TG_NARGS,
	PLPGSQL_PROMISE_TG_ARGV,
	PLPGSQL_PROMISE_TG_EVENT,
	PLPGSQL_PROMISE_TG_TAG
} PLpgSQL_promise_type;

/*
 * 在 PLpgSQL_type 结构中区分的变体
 */
typedef enum PLpgSQL_type_type
{
	PLPGSQL_TTYPE_SCALAR,		/* 标量类型和域 */
	PLPGSQL_TTYPE_REC,			/* 复合类型，包括 RECORD */
	PLPGSQL_TTYPE_PSEUDO		/* 伪类型 */
} PLpgSQL_type_type;

/*
 * 执行树节点类型
 */
typedef enum PLpgSQL_stmt_type
{
	PLPGSQL_STMT_BLOCK,
	PLPGSQL_STMT_ASSIGN,
	PLPGSQL_STMT_IF,
	PLPGSQL_STMT_CASE,
	PLPGSQL_STMT_LOOP,
	PLPGSQL_STMT_WHILE,
	PLPGSQL_STMT_FORI,
	PLPGSQL_STMT_FORS,
	PLPGSQL_STMT_FORC,
	PLPGSQL_STMT_FOREACH_A,
	PLPGSQL_STMT_EXIT,
	PLPGSQL_STMT_RETURN,
	PLPGSQL_STMT_RETURN_NEXT,
	PLPGSQL_STMT_RETURN_QUERY,
	PLPGSQL_STMT_RAISE,
	PLPGSQL_STMT_ASSERT,
	PLPGSQL_STMT_EXECSQL,
	PLPGSQL_STMT_DYNEXECUTE,
	PLPGSQL_STMT_DYNFORS,
	PLPGSQL_STMT_GETDIAG,
	PLPGSQL_STMT_OPEN,
	PLPGSQL_STMT_FETCH,
	PLPGSQL_STMT_CLOSE,
	PLPGSQL_STMT_PERFORM,
	PLPGSQL_STMT_CALL,
	PLPGSQL_STMT_COMMIT,
	PLPGSQL_STMT_ROLLBACK
} PLpgSQL_stmt_type;

/*
 * 执行节点返回代码
 */
enum
{
	PLPGSQL_RC_OK,
	PLPGSQL_RC_EXIT,
	PLPGSQL_RC_RETURN,
	PLPGSQL_RC_CONTINUE
};

/*
 * GET DIAGNOSTICS 信息项
 */
typedef enum PLpgSQL_getdiag_kind
{
	PLPGSQL_GETDIAG_ROW_COUNT,
	PLPGSQL_GETDIAG_CONTEXT,
	PLPGSQL_GETDIAG_ERROR_CONTEXT,
	PLPGSQL_GETDIAG_ERROR_DETAIL,
	PLPGSQL_GETDIAG_ERROR_HINT,
	PLPGSQL_GETDIAG_RETURNED_SQLSTATE,
	PLPGSQL_GETDIAG_COLUMN_NAME,
	PLPGSQL_GETDIAG_CONSTRAINT_NAME,
	PLPGSQL_GETDIAG_DATATYPE_NAME,
	PLPGSQL_GETDIAG_MESSAGE_TEXT,
	PLPGSQL_GETDIAG_TABLE_NAME,
	PLPGSQL_GETDIAG_SCHEMA_NAME
} PLpgSQL_getdiag_kind;

/*
 * RAISE 语句选项
 */
typedef enum PLpgSQL_raise_option_type
{
	PLPGSQL_RAISEOPTION_ERRCODE,
	PLPGSQL_RAISEOPTION_MESSAGE,
	PLPGSQL_RAISEOPTION_DETAIL,
	PLPGSQL_RAISEOPTION_HINT,
	PLPGSQL_RAISEOPTION_COLUMN,
	PLPGSQL_RAISEOPTION_CONSTRAINT,
	PLPGSQL_RAISEOPTION_DATATYPE,
	PLPGSQL_RAISEOPTION_TABLE,
	PLPGSQL_RAISEOPTION_SCHEMA
} PLpgSQL_raise_option_type;

/*
 * plpgsql 变量解析的行为模式
 */
typedef enum PLpgSQL_resolve_option
{
	PLPGSQL_RESOLVE_ERROR,		/* 如果不明确则抛出错误 */
	PLPGSQL_RESOLVE_VARIABLE,	/* 优先使用 plpgsql 变量而非表列 */
	PLPGSQL_RESOLVE_COLUMN		/* 优先使用表列而非 plpgsql 变量 */
} PLpgSQL_resolve_option;


/**********************************************************************
 * 节点和结构定义
 **********************************************************************/

/*
 * Postgres 数据类型
 */
typedef struct PLpgSQL_type
{
	char	   *typname;		/* （简单）类型名称 */
	Oid			typoid;			/* 数据类型的 OID */
	PLpgSQL_type_type ttype;	/* PLPGSQL_TTYPE_ 代码 */
	int16		typlen;			/* 从 pg_type 项复制的内容 */
	bool		typbyval;
	char		typtype;
	Oid			collation;		/* 来自 pg_type，但可以被覆盖 */
	bool		typisarray;		/* 是“真实”数组，或其上的域 */
	int32		atttypmod;		/* typmod（来自其他地方） */
	/* 剩余字段仅用于命名的复合类型（不是 RECORD） */
	TypeName   *origtypname;	/* 用户写的类型名称 */
	TypeCacheEntry *tcache;		/* 复合类型的 typcache 条目 */
	uint64		tupdesc_id;		/* 最近看到的 tupdesc 标识符 */
} PLpgSQL_type;

/*
 * 要计划和执行的 SQL 查询
 */
typedef struct PLpgSQL_expr
{
	char	   *query;			/* 查询字符串，逐字来自函数主体 */
	RawParseMode parseMode;		/* 使用的 raw_parser() 模式 */
	SPIPlanPtr	plan;			/* 计划，如果尚未创建则为 NULL */
	Bitmapset  *paramnos;		/* 此查询引用的所有 dnos */

	/* 包含此表达式的函数（在我们第一次解析查询之前未设置） */
	struct PLpgSQL_function *func;

	/* 对此表达式可见的命名空间链 */
	struct PLpgSQL_nsitem *ns;

	/* "简单表达式" 快速路径执行的字段： */
	Expr	   *expr_simple_expr;	/* NULL 表示不是简单表达式 */
	Oid			expr_simple_type;	/* 如果是简单表达式，则结果类型 Oid */
	int32		expr_simple_typmod; /* 如果是简单表达式，则结果 typmod */
	bool		expr_simple_mutable;	/* 如果简单表达式是可变的则为真 */

	/*
	 * 这些字段用于优化对扩展数据
	 * 变量的赋值。如果此表达式是对简单变量赋值的源，
	 * target_param 保存该变量的 dno；否则为 -1。
	 * 如果我们在 expr_simple_expr 中匹配到一个 Param
	 * 到这样的变量，则该 Param 的地址存储在 expr_rw_param 中；
	 * 然后表达式代码生成将允许该 Param 的值被传递为读/写。
	 */
	int			target_param;	/* 赋值目标的 dno，或 -1 如果没有 */
	Param	   *expr_rw_param;	/* 读取/写入表达式中的参数（如果有） */

	/*
	 * 如果表达式曾经被判断为简单，我们将其
	 * CachedPlanSource 和 CachedPlan 存储在此处。 如果 expr_simple_plan_lxid 与
	 * 当前 LXID 匹配，则我们在当前事务中保持对 expr_simple_plan 的引用计数。
	 * 否则，我们需要在重新使用之前获取一个。
	 */
	CachedPlanSource *expr_simple_plansource;	/* 从 "plan" 中提取 */
	CachedPlan *expr_simple_plan;	/* 从 "plan" 中提取 */
	LocalTransactionId expr_simple_plan_lxid;

	/*
	 * 如果 expr 是简单的并且在当前事务中已准备，
	 * expr_simple_state 和 expr_simple_in_use 是有效的。 通过
	 * 查看 expr_simple_lxid 是否与当前 LXID 匹配来测试有效性。 （如果不匹配，
	 * expr_simple_state 可能指向垃圾数据！）
	 */
	ExprState  *expr_simple_state;	/* expr_simple_expr 的评估树 */
	bool		expr_simple_in_use; /* 如果评估树是活动的，则为真 */
	LocalTransactionId expr_simple_lxid;
} PLpgSQL_expr;

/*
 * 通用数据项数组
 *
 * PLpgSQL_datum 是 PLpgSQL_var、PLpgSQL_row、
 * PLpgSQL_rec 和 PLpgSQL_recfield 的公共超类型。
 */
typedef struct PLpgSQL_datum
{
	PLpgSQL_datum_type dtype;
	int			dno;
} PLpgSQL_datum;

/*
 * 标量或复合变量
 *
 * PLpgSQL_var、PLpgSQL_row 和 PLpgSQL_rec 共享这些
 * 字段。
 */
typedef struct PLpgSQL_variable
{
	PLpgSQL_datum_type dtype;
	int			dno;
	char	   *refname;
	int			lineno;
	bool		isconst;
	bool		notnull;
	PLpgSQL_expr *default_val;
} PLpgSQL_variable;

/*
 * 标量变量
 *
 * DTYPE_VAR 和 DTYPE_PROMISE 数据项都使用此结构类型。
 * PROMISE 数据项在大多数情况下的工作方式与 VAR 数据项完全相同，
 * 但如果在此前未赋值的情况下读取，则在执行读取之前
 * 将计算并赋值一个特殊的“承诺”值给数据项。 这种技术在我们预期
 * 许多函数将永远不会读取该变量的情况下，避免了计算变量值的开销。
 */
typedef struct PLpgSQL_var
{
	PLpgSQL_datum_type dtype;
	int			dno;
	char	   *refname;
	int			lineno;
	bool		isconst;
	bool		notnull;
	PLpgSQL_expr *default_val;
	/* PLpgSQL_variable 字段的结束 */

	PLpgSQL_type *datatype;

	/*
	 * 声明为 CURSOR FOR <query> 的变量与普通
	 * refcursor 类型的标量变量大致相似，但它们具有以下附加
	 * 属性：
	 */
	PLpgSQL_expr *cursor_explicit_expr;
	int			cursor_explicit_argrow;
	int			cursor_options;

	/* 下面的字段可以在运行时更改 */

	Datum		value;
	bool		isnull;
	bool		freeval;

	/*
	 * promise 字段记录在必须履行承诺时
	 * 要分配的“承诺”值。如果它是普通变量，或承诺已
	 * 履行，则为 PLPGSQL_PROMISE_NONE。
	 */
	PLpgSQL_promise_type promise;
} PLpgSQL_var;

/*
 * 行变量 - 这表示在 INTO 子句、FOR 循环目标列表、游标参数列表等中列出的一项或多项变量。
 * 当存在多个OUT参数时，我们还使用行来表示函数的OUT参数。
 *
 * 请注意，从 PL/pgSQL 代码中没有办法将行命名为这样的名称，
 * 因此许多函数不需要支持这些。
 *
 * 这也意味着行变量没有真实名称，因此我们
 * 按慣例将 refname 设置为 "(unnamed row)"。 我们可以将其留为 NULL，
 * 但是能够假设所有 PLpgSQL_variable 的变体中的 refname 是有效的
 * 是非常方便的。
 *
 * isconst、notnull 和 default_val 在行中不受支持（因此
 * 总是为零/空）。 行的成员变量应在编译时已被检查为
 * 可写，因此 isconst 正确地设置为 false。 notnull 和 default_val 不适用。
 */
typedef struct PLpgSQL_row
{
	PLpgSQL_datum_type dtype;
	int			dno;
	char	   *refname;
	int			lineno;
	bool		isconst;
	bool		notnull;
	PLpgSQL_expr *default_val;
	/* PLpgSQL_variable 字段的结束 */

	/*
	 * rowtupdesc 仅在我们可能需要将行转换为复合数据时设置，
	 * 目前这仅发生在 OUT 参数上。
	 * 否则它为 NULL。
	 */
	TupleDesc	rowtupdesc;

	int			nfields;
	char	  **fieldnames;
	int		   *varnos;
} PLpgSQL_row;

/*
 * 记录变量（任何复合类型，包括 RECORD）
 */
typedef struct PLpgSQL_rec
{
	PLpgSQL_datum_type dtype;
	int			dno;
	char	   *refname;
	int			lineno;
	bool		isconst;
	bool		notnull;
	PLpgSQL_expr *default_val;
	/* PLpgSQL_variable 字段的结束 */

	/*
	 * 注意：对于非 RECORD 的情况，我们可能不时重新查找
	 * 复合类型，使用 datatype->origtypname。 这可能导致
	 * 更改 rectypeid。
	 */

	PLpgSQL_type *datatype;		/* 如果 rectypeid 是 RECORDOID，则可以为 NULL */
	Oid			rectypeid;		/* 变量的声明类型 */
	/* 该记录的 RECFIELD 通过链表连接以便于访问 */
	int			firstfield;		/* 第一个 RECFIELD 的 dno，如果没有则为 -1 */

	/* 下面的字段可以在运行时更改 */

	/* 我们始终将记录变量存储为“展开”记录 */
	ExpandedRecordHeader *erh;
} PLpgSQL_rec;

/*
 * 记录中的字段
 */
typedef struct PLpgSQL_recfield
{
	PLpgSQL_datum_type dtype;
	int			dno;
	/* PLpgSQL_datum 字段的结束 */

	char	   *fieldname;		/* 字段名称 */
	int			recparentno;	/* 父记录的 dno */
	int			nextfield;		/* 下一个子记录的 dno，如果没有则为 -1 */
	uint64		rectupledescid; /* 记录的 tupledesc ID，最后一次查找时的 ID */
	ExpandedRecordFieldInfo finfo;	/* 字段的 attnum 和类型信息 */
	/* 如果 rectupledescid == INVALID_TUPLEDESC_IDENTIFIER，则 finfo 无效 */
} PLpgSQL_recfield;

/*
 * 编译器命名空间树中的项
 */
typedef struct PLpgSQL_nsitem
{
	PLpgSQL_nsitem_type itemtype;

	/*
	 * 对于标签，itemno 是 PLpgSQL_label_type 的枚举值。对于其他
	 * itemtypes，itemno 是关联的 PLpgSQL_datum 的 dno。
	 */
	int			itemno;
	struct PLpgSQL_nsitem *prev;
	char		name[FLEXIBLE_ARRAY_MEMBER];	/* 以 null 结尾的字符串 */
} PLpgSQL_nsitem;

/*
 * 通用执行节点
 */
typedef struct PLpgSQL_stmt
{
	PLpgSQL_stmt_type cmd_type;
	int			lineno;

	/*
	 * 该函数中的唯一语句 ID（从 1 开始；0 为无效/未
	 * 设置）。这可以被分析器用作每个语句指标数组的索引。
	 */
	unsigned int stmtid;
} PLpgSQL_stmt;

/*
 * 一个 EXCEPTION 条件名称
 */
typedef struct PLpgSQL_condition
{
	int			sqlerrstate;	/* SQLSTATE 代码 */
	char	   *condname;		/* 条件名称（用于调试） */
	struct PLpgSQL_condition *next;
} PLpgSQL_condition;

/*
 * EXCEPTION 块
 */
typedef struct PLpgSQL_exception_block
{
	int			sqlstate_varno;
	int			sqlerrm_varno;
	List	   *exc_list;		/* WHEN 子句列表 */
} PLpgSQL_exception_block;

/*
 * 一个 EXCEPTION ... WHEN 子句
 */
typedef struct PLpgSQL_exception
{
	int			lineno;
	PLpgSQL_condition *conditions;
	List	   *action;			/* 语句列表 */
} PLpgSQL_exception;

/*
 * 语句块
 */
typedef struct PLpgSQL_stmt_block
{
	PLpgSQL_stmt_type cmd_type;
	int			lineno;
	unsigned int stmtid;
	char	   *label;
	List	   *body;			/* 语句列表 */
	int			n_initvars;		/* initvarnos[] 的长度 */
	int		   *initvarnos;		/* 在此块中声明的变量的 dnos */
	PLpgSQL_exception_block *exceptions;
} PLpgSQL_stmt_block;

/*
 * 赋值语句
 */
typedef struct PLpgSQL_stmt_assign
{
	PLpgSQL_stmt_type cmd_type;
	int			lineno;
	unsigned int stmtid;
	int			varno;
	PLpgSQL_expr *expr;
} PLpgSQL_stmt_assign;

/*
 * PERFORM 语句
 */
typedef struct PLpgSQL_stmt_perform
{
	PLpgSQL_stmt_type cmd_type;
	int			lineno;
	unsigned int stmtid;
	PLpgSQL_expr *expr;
} PLpgSQL_stmt_perform;

/*
 * CALL 语句
 */
typedef struct PLpgSQL_stmt_call
{
	PLpgSQL_stmt_type cmd_type;
	int			lineno;
	unsigned int stmtid;
	PLpgSQL_expr *expr;
	bool		is_call;
	PLpgSQL_variable *target;
} PLpgSQL_stmt_call;

/*
 * COMMIT 语句
 */
typedef struct PLpgSQL_stmt_commit
{
	PLpgSQL_stmt_type cmd_type;
	int			lineno;
	unsigned int stmtid;
	bool		chain;
} PLpgSQL_stmt_commit;

/*
 * ROLLBACK 语句
 */
typedef struct PLpgSQL_stmt_rollback
{
	PLpgSQL_stmt_type cmd_type;
	int			lineno;
	unsigned int stmtid;
	bool		chain;
} PLpgSQL_stmt_rollback;

/*
 * GET DIAGNOSTICS 项
 */
typedef struct PLpgSQL_diag_item
{
	PLpgSQL_getdiag_kind kind;	/* 所需诊断值的 id */
	int			target;			/* 分配它的位置 */
} PLpgSQL_diag_item;

/*
 * GET DIAGNOSTICS 语句
 */
typedef struct PLpgSQL_stmt_getdiag
{
	PLpgSQL_stmt_type cmd_type;
	int			lineno;
	unsigned int stmtid;
	bool		is_stacked;		/* STACKED 或 CURRENT 诊断区域？ */
	List	   *diag_items;		/* PLpgSQL_diag_item 列表 */
} PLpgSQL_stmt_getdiag;

/*
 * IF 语句
 */
typedef struct PLpgSQL_stmt_if
{
	PLpgSQL_stmt_type cmd_type;
	int			lineno;
	unsigned int stmtid;
	PLpgSQL_expr *cond;			/* THEN 的布尔表达式 */
	List	   *then_body;		/* 语句列表 */
	List	   *elsif_list;		/* PLpgSQL_if_elsif 结构的列表 */
	List	   *else_body;		/* 语句列表 */
} PLpgSQL_stmt_if;

/*
 * IF 语句的一个 ELSIF 分支
 */
typedef struct PLpgSQL_if_elsif
{
	int			lineno;
	PLpgSQL_expr *cond;			/* 此情况的布尔表达式 */
	List	   *stmts;			/* 语句列表 */
} PLpgSQL_if_elsif;

/*
 * CASE 语句
 */
typedef struct PLpgSQL_stmt_case
{
	PLpgSQL_stmt_type cmd_type;
	int			lineno;
	unsigned int stmtid;
	PLpgSQL_expr *t_expr;		/* 测试表达式，如果没有则为 NULL */
	int			t_varno;		/* 用于存储测试表达式值的变量 */
	List	   *case_when_list; /* PLpgSQL_case_when 结构的列表 */
	bool		have_else;		/* 需要的标志，因为列表可能为空 */
	List	   *else_stmts;		/* 语句列表 */
} PLpgSQL_stmt_case;

/*
 * CASE 语句的一个分支
 */
typedef struct PLpgSQL_case_when
{
	int			lineno;
	PLpgSQL_expr *expr;			/* 此情况的布尔表达式 */
	List	   *stmts;			/* 语句列表 */
} PLpgSQL_case_when;

/*
 * 无条件的循环语句
 */
typedef struct PLpgSQL_stmt_loop
{
	PLpgSQL_stmt_type cmd_type;
	int			lineno;
	unsigned int stmtid;
	char	   *label;
	List	   *body;			/* 语句列表 */
} PLpgSQL_stmt_loop;

/*
 * 条件循环语句
 */
typedef struct PLpgSQL_stmt_while
{
	PLpgSQL_stmt_type cmd_type;
	int			lineno;
	unsigned int stmtid;
	char	   *label;
	PLpgSQL_expr *cond;
	List	   *body;			/* 语句列表 */
} PLpgSQL_stmt_while;

/*
 * 使用整数循环变量的FOR语句
 */
typedef struct PLpgSQL_stmt_fori
{
	PLpgSQL_stmt_type cmd_type;
	int			lineno;
	unsigned int stmtid;
	char	   *label;
	PLpgSQL_var *var;
	PLpgSQL_expr *lower;
	PLpgSQL_expr *upper;
	PLpgSQL_expr *step;			/* NULL表示默认（即，BY 1） */
	int			reverse;
	List	   *body;			/* 语句列表 */
} PLpgSQL_stmt_fori;

/*
 * PLpgSQL_stmt_forq表示在SQL查询上运行的FOR语句。
 * 它是PLpgSQL_stmt_fors、PLpgSQL_stmt_forc
 * 和PLpgSQL_stmt_dynfors的共同超类型。
 */
typedef struct PLpgSQL_stmt_forq
{
	PLpgSQL_stmt_type cmd_type;
	int			lineno;
	unsigned int stmtid;
	char	   *label;
	PLpgSQL_variable *var;		/* 循环变量（记录或行） */
	List	   *body;			/* 语句列表 */
} PLpgSQL_stmt_forq;

/*
 * 在SELECT上运行的FOR语句
 */
typedef struct PLpgSQL_stmt_fors
{
	PLpgSQL_stmt_type cmd_type;
	int			lineno;
	unsigned int stmtid;
	char	   *label;
	PLpgSQL_variable *var;		/* 循环变量（记录或行） */
	List	   *body;			/* 语句列表 */
	/* 必须与PLpgSQL_stmt_forq匹配的字段的结束 */
	PLpgSQL_expr *query;
} PLpgSQL_stmt_fors;

/*
 * 在游标上运行的FOR语句
 */
typedef struct PLpgSQL_stmt_forc
{
	PLpgSQL_stmt_type cmd_type;
	int			lineno;
	unsigned int stmtid;
	char	   *label;
	PLpgSQL_variable *var;		/* 循环变量（记录或行） */
	List	   *body;			/* 语句列表 */
	/* 必须与PLpgSQL_stmt_forq匹配的字段的结束 */
	int			curvar;
	PLpgSQL_expr *argquery;		/* 游标参数（如果有的话） */
} PLpgSQL_stmt_forc;

/*
 * 在EXECUTE上运行的FOR语句
 */
typedef struct PLpgSQL_stmt_dynfors
{
	PLpgSQL_stmt_type cmd_type;
	int			lineno;
	unsigned int stmtid;
	char	   *label;
	PLpgSQL_variable *var;		/* 循环变量（记录或行） */
	List	   *body;			/* 语句列表 */
	/* 必须与PLpgSQL_stmt_forq匹配的字段的结束 */
	PLpgSQL_expr *query;
	List	   *params;			/* 使用表达式 */
} PLpgSQL_stmt_dynfors;

/*
 * 在数组中循环的FOREACH语句
 */
typedef struct PLpgSQL_stmt_foreach_a
{
	PLpgSQL_stmt_type cmd_type;
	int			lineno;
	unsigned int stmtid;
	char	   *label;
	int			varno;			/* 循环目标变量 */
	int			slice;			/* 切片维度，或0 */
	PLpgSQL_expr *expr;			/* 数组表达式 */
	List	   *body;			/* 语句列表 */
} PLpgSQL_stmt_foreach_a;

/*
 * 打开一个游标变量
 */
typedef struct PLpgSQL_stmt_open
{
	PLpgSQL_stmt_type cmd_type;
	int			lineno;
	unsigned int stmtid;
	int			curvar;
	int			cursor_options;
	PLpgSQL_expr *argquery;
	PLpgSQL_expr *query;
	PLpgSQL_expr *dynquery;
	List	   *params;			/* 使用表达式 */
} PLpgSQL_stmt_open;

/*
 * FETCH或MOVE语句
 */
typedef struct PLpgSQL_stmt_fetch
{
	PLpgSQL_stmt_type cmd_type;
	int			lineno;
	unsigned int stmtid;
	PLpgSQL_variable *target;	/* 目标（记录或行） */
	int			curvar;			/* 从中获取的游标变量 */
	FetchDirection direction;	/* 获取方向 */
	long		how_many;		/* 计数，如果常量（表达式为NULL） */
	PLpgSQL_expr *expr;			/* 计数，如果是表达式 */
	bool		is_move;		/* 这是获取还是移动？ */
	bool		returns_multiple_rows;	/* 可以返回多于一行吗？ */
} PLpgSQL_stmt_fetch;

/*
 * 关闭游标变量
 */
typedef struct PLpgSQL_stmt_close
{
	PLpgSQL_stmt_type cmd_type;
	int			lineno;
	unsigned int stmtid;
	int			curvar;
} PLpgSQL_stmt_close;

/*
 * EXIT或CONTINUE语句
 */
typedef struct PLpgSQL_stmt_exit
{
	PLpgSQL_stmt_type cmd_type;
	int			lineno;
	unsigned int stmtid;
	bool		is_exit;		/* 这是退出还是继续？ */
	char	   *label;			/* 如果是未标记的EXIT/CONTINUE则为NULL */
	PLpgSQL_expr *cond;
} PLpgSQL_stmt_exit;

/*
 * RETURN语句
 */
typedef struct PLpgSQL_stmt_return
{
	PLpgSQL_stmt_type cmd_type;
	int			lineno;
	unsigned int stmtid;
	PLpgSQL_expr *expr;
	int			retvarno;
} PLpgSQL_stmt_return;

/*
 * RETURN NEXT语句
 */
typedef struct PLpgSQL_stmt_return_next
{
	PLpgSQL_stmt_type cmd_type;
	int			lineno;
	unsigned int stmtid;
	PLpgSQL_expr *expr;
	int			retvarno;
} PLpgSQL_stmt_return_next;

/*
 * RETURN QUERY语句
 */
typedef struct PLpgSQL_stmt_return_query
{
	PLpgSQL_stmt_type cmd_type;
	int			lineno;
	unsigned int stmtid;
	PLpgSQL_expr *query;		/* 如果是静态查询 */
	PLpgSQL_expr *dynquery;		/* 如果是动态查询（RETURN QUERY EXECUTE） */
	List	   *params;			/* 动态查询的使用参数 */
} PLpgSQL_stmt_return_query;

/*
 * RAISE语句
 */
typedef struct PLpgSQL_stmt_raise
{
	PLpgSQL_stmt_type cmd_type;
	int			lineno;
	unsigned int stmtid;
	int			elog_level;
	char	   *condname;		/* 条件名称、SQLSTATE或NULL */
	char	   *message;		/* 老式消息格式字面量或NULL */
	List	   *params;			/* 老式消息的表达式列表 */
	List	   *options;		/* PLpgSQL_raise_option的列表 */
} PLpgSQL_stmt_raise;

/*
 * RAISE语句选项
 */
typedef struct PLpgSQL_raise_option
{
	PLpgSQL_raise_option_type opt_type;
	PLpgSQL_expr *expr;
} PLpgSQL_raise_option;

/*
 * ASSERT语句
 */
typedef struct PLpgSQL_stmt_assert
{
	PLpgSQL_stmt_type cmd_type;
	int			lineno;
	unsigned int stmtid;
	PLpgSQL_expr *cond;
	PLpgSQL_expr *message;
} PLpgSQL_stmt_assert;

/*
 * 要执行的通用SQL语句
 */
typedef struct PLpgSQL_stmt_execsql
{
	PLpgSQL_stmt_type cmd_type;
	int			lineno;
	unsigned int stmtid;
	PLpgSQL_expr *sqlstmt;
	bool		mod_stmt;		/* stmt是INSERT/UPDATE/DELETE/MERGE吗？ */
	bool		mod_stmt_set;	/* mod_stmt是否有效？ */
	bool		into;			/* 提供了INTO吗？ */
	bool		strict;			/* INTO严格标志 */
	PLpgSQL_variable *target;	/* INTO目标（记录或行） */
} PLpgSQL_stmt_execsql;

/*
 * 要执行的动态SQL字符串
 */
typedef struct PLpgSQL_stmt_dynexecute
{
	PLpgSQL_stmt_type cmd_type;
	int			lineno;
	unsigned int stmtid;
	PLpgSQL_expr *query;		/* 字符串表达式 */
	bool		into;			/* 提供了INTO吗？ */
	bool		strict;			/* INTO严格标志 */
	PLpgSQL_variable *target;	/* INTO目标（记录或行） */
	List	   *params;			/* 使用表达式 */
} PLpgSQL_stmt_dynexecute;

/*
 * 函数的哈希查找键
 */
typedef struct PLpgSQL_func_hashkey
{
	Oid			funcOid;

	bool		isTrigger;		/* 如果作为DML触发器调用则为true */
	bool		isEventTrigger; /* 如果作为事件触发器调用则为true */

	/* 小心这个结构中的填充字节被置为零！ */

	/*
	 * 对于触发函数，触发器的OID是哈希键的一部分
	 * --- 我们希望为每个它使用的触发器单独编译触发函数，
	 * 以防行类型或转换表名称不同。 如果不是作为DML触发器调用则为零。
	 */
	Oid			trigOid;

	/*
	 * 我们必须将输入排序规则作为哈希键的一部分也包含在内，
	 * 因为我们必须为不同的排序设置生成不同的计划（具有不同的参数
	 * 排序规则）。
	 */
	Oid			inputCollation;

	/*
	 * 我们在哈希键中包含实际参数类型以支持多态
	 * PLpgSQL函数。 小心额外的位置被置为零！
	 */
	Oid			argtypes[FUNC_MAX_ARGS];
} PLpgSQL_func_hashkey;

/*
 * 触发器类型
 */
typedef enum PLpgSQL_trigtype
{
	PLPGSQL_DML_TRIGGER,
	PLPGSQL_EVENT_TRIGGER,
	PLPGSQL_NOT_TRIGGER
} PLpgSQL_trigtype;

/*
 * 完整编译函数
 */
typedef struct PLpgSQL_function
{
	char	   *fn_signature;
	Oid			fn_oid;
	TransactionId fn_xmin;
	ItemPointerData fn_tid;
	PLpgSQL_trigtype fn_is_trigger;
	Oid			fn_input_collation;
	PLpgSQL_func_hashkey *fn_hashkey;	/* 指向哈希表键的反向链接 */
	MemoryContext fn_cxt;

	Oid			fn_rettype;
	int			fn_rettyplen;
	bool		fn_retbyval;
	bool		fn_retistuple;
	bool		fn_retisdomain;
	bool		fn_retset;
	bool		fn_readonly;
	char		fn_prokind;

	int			fn_nargs;
	int			fn_argvarnos[FUNC_MAX_ARGS];
	int			out_param_varno;
	int			found_varno;
	int			new_varno;
	int			old_varno;

	PLpgSQL_resolve_option resolve_option;

	bool		print_strict_params;

	/* 附加检查 */
	int			extra_warnings;
	int			extra_errors;

	/* 代表函数局部变量的数据项 */
	int			ndatums;
	PLpgSQL_datum **datums;
	Size		copiable_size;	/* 用于局部实例化数据项的空间 */

	/* 函数体解析树 */
	PLpgSQL_stmt_block *action;

	/* 解析主体时派生的数据 */
	unsigned int nstatements;	/* 分配stmtids的计数器 */
	bool		requires_procedure_resowner;	/* 包含调用CALL还是DO？ */

	/* 这些字段在函数使用时会改变 */
	struct PLpgSQL_execstate *cur_estate;
	unsigned long use_count;
} PLpgSQL_function;

/*
 * 运行时执行数据
 */
typedef struct PLpgSQL_execstate
{
	PLpgSQL_function *func;		/* 正在执行的函数 */

	TriggerData *trigdata;		/* 如果是常规触发器，关于触发的数据 */
	EventTriggerData *evtrigdata;	/* 如果是事件触发器，关于触发的数据 */

	Datum		retval;
	bool		retisnull;
	Oid			rettype;		/* 当前返回值类型 */

	Oid			fn_rettype;		/* 关于声明函数返回类型的信息 */
	bool		retistuple;
	bool		retisset;

	bool		readonly_func;
	bool		atomic;

	char	   *exitlabel;		/* 当前EXIT或CONTINUE语句的“目标”标签（如果有的话） */
	ErrorData  *cur_error;		/* 当前异常处理程序的错误 */

	Tuplestorestate *tuple_store;	/* SRFs在这里累积结果 */
	TupleDesc	tuple_store_desc;	/* 存储元组的元组_store描述符 */
	MemoryContext tuple_store_cxt;
	ResourceOwner tuple_store_owner;
	ReturnSetInfo *rsi;

	int			found_varno;

	/*
	 * 代表函数局部变量的数据项。 这些中的一些
	 * 是这个execstate中的局部存储，但有些只是指向属于
	 * PLpgSQL_function的共享副本，具体取决于我们
	 * 是否需要datum的dtype的每次执行状态。
	 */
	int			ndatums;
	PLpgSQL_datum **datums;
	/* 包含变量值的上下文（与函数的SPI_proc上下文相同） */
	MemoryContext datum_context;

	/*
	 * paramLI是我们用于将局部变量值传递给执行者的。
	 * 它没有ParamExternData数组；我们只是根据需要动态
	 * 实例化参数数据。 按约定，PARAM_EXTERN
	 * 参数的paramid等于被引用局部变量的dno。
	 */
	ParamListInfo paramLI;

	/* 用于“简单”表达式评估的EState和resowner */
	EState	   *simple_eval_estate;
	ResourceOwner simple_eval_resowner;

	/* 如果运行非原子过程或DO块，调用的resowner */
	ResourceOwner procedure_resowner;

	/* 用于执行类型转换的查找表 */
	HTAB	   *cast_hash;
	MemoryContext cast_hash_context;	/* 不使用；现在始终为NULL */

	/* 用于语句生命周期临时值的内存上下文 */
	MemoryContext stmt_mcontext;	/* 当前语句上下文，如果没有则为NULL */
	MemoryContext stmt_mcontext_parent; /* 当前上下文的父上下文 */

	/* 临时状态，用于查询或表达式评估的结果 */
	SPITupleTable *eval_tuptable;
	uint64		eval_processed;
	ExprContext *eval_econtext; /* 用于执行简单表达式 */

	/* 错误上下文报告的状态信息 */
	PLpgSQL_stmt *err_stmt;		/* 当前语句 */
	PLpgSQL_variable *err_var;	/* 当前变量，如果在DECLARE部分中 */
	const char *err_text;		/* 附加状态信息 */

	void	   *plugin_info;	/* 预留给可选插件使用 */
} PLpgSQL_execstate;

/*
 * A PLpgSQL_plugin结构表示一个Instrumentation插件。
 * 要对PL/pgSQL进行Instrumentation，插件库必须访问
 * 变量“PLpgSQL_plugin”并将其设置为指向PLpgSQL_plugin结构。
 * 通常，该结构可以仅是插件库中的静态数据。
 * 我们预计插件将在库加载时（_PG_init()）执行此操作。
 *
 * 该结构基本上是函数指针的集合——在pl_exec.c中的各种
 * 重要时刻，我们调用这些函数（如果指针非空），以便给插件
 * 一个观察我们正在做什么的机会。
 *
 * func_setup在我们启动一个函数时调用，在我们初始化
 * 函数定义的局部变量之前。
 *
 * func_beg在我们启动一个函数时调用，在我们初始化
 * 局部变量之后。
 *
 * func_end在函数结束时调用。
 *
 * stmt_beg和stmt_end分别在每个语句之前和之后调用。
 *
 * 此外，在任何对func_setup的调用之前，PL/pgSQL会填充
 * 剩余字段为其自身的一些函数的指针，使插件能够方便地
 * 调用这些函数。公开的函数包括：
 *		plpgsql_exec_error_callback
 *		exec_assign_expr
 *		exec_assign_value
 *		exec_eval_datum
 *		exec_cast_value
 * (plpgsql_exec_error_callback实际上并不打算被
 * 插件调用，而是允许它识别PL/pgSQL错误上下文堆栈
 * 帧。其他函数对于调试器类插件检查和设置变量
 * 是有用的。)
 */
typedef struct PLpgSQL_plugin
{
	/* 由插件设置的函数指针 */
	void		(*func_setup) (PLpgSQL_execstate *estate, PLpgSQL_function *func);
	void		(*func_beg) (PLpgSQL_execstate *estate, PLpgSQL_function *func);
	void		(*func_end) (PLpgSQL_execstate *estate, PLpgSQL_function *func);
	void		(*stmt_beg) (PLpgSQL_execstate *estate, PLpgSQL_stmt *stmt);
	void		(*stmt_end) (PLpgSQL_execstate *estate, PLpgSQL_stmt *stmt);

	/* PL/pgSQL自身设置的函数指针 */
	void		(*error_callback) (void *arg);
	void		(*assign_expr) (PLpgSQL_execstate *estate,
								PLpgSQL_datum *target,
								PLpgSQL_expr *expr);
	void		(*assign_value) (PLpgSQL_execstate *estate,
								 PLpgSQL_datum *target,
								 Datum value, bool isNull,
								 Oid valtype, int32 valtypmod);
	void		(*eval_datum) (PLpgSQL_execstate *estate, PLpgSQL_datum *datum,
							   Oid *typeId, int32 *typetypmod,
							   Datum *value, bool *isnull);
	Datum		(*cast_value) (PLpgSQL_execstate *estate,
							   Datum value, bool *isnull,
							   Oid valtype, int32 valtypmod,
							   Oid reqtype, int32 reqtypmod);
} PLpgSQL_plugin;

/*
 * 在解析期间使用的结构类型
 */

typedef struct PLword
{
	char	   *ident;			/* palloc分配的转换标识符 */
	bool		quoted;			/* 它是双引号吗？ */
} PLword;

typedef struct PLcword
{
	List	   *idents;			/* 复合标识符（字符串列表） */
} PLcword;

typedef struct PLwdatum
{
	PLpgSQL_datum *datum;		/* 引用的变量 */
	char	   *ident;			/* 如果是简单名称则有效 */
	bool		quoted;
	List	   *idents;			/* 如果是复合名称则有效 */
} PLwdatum;

/**********************************************************************
 * 全局变量声明
 **********************************************************************/

typedef enum
{
	IDENTIFIER_LOOKUP_NORMAL,	/* 变量名称的正常处理 */
	IDENTIFIER_LOOKUP_DECLARE,	/* 在DECLARE中——不要查找名称 */
	IDENTIFIER_LOOKUP_EXPR		/* 在SQL表达式中——特例 */
} IdentifierLookup;

extern IdentifierLookup plpgsql_IdentifierLookup;

extern int	plpgsql_variable_conflict;

extern bool plpgsql_print_strict_params;

extern bool plpgsql_check_asserts;

/* 额外的编译时和运行时检查 */
#define PLPGSQL_XCHECK_NONE						0
#define PLPGSQL_XCHECK_SHADOWVAR				(1 << 1)
#define PLPGSQL_XCHECK_TOOMANYROWS				(1 << 2)
#define PLPGSQL_XCHECK_STRICTMULTIASSIGNMENT	(1 << 3)
#define PLPGSQL_XCHECK_ALL						((int) ~0)

extern int	plpgsql_extra_warnings;
extern int	plpgsql_extra_errors;

extern bool plpgsql_check_syntax;
extern bool plpgsql_DumpExecTree;

extern PLpgSQL_stmt_block *plpgsql_parse_result;

extern int	plpgsql_nDatums;
extern PLpgSQL_datum **plpgsql_Datums;

extern char *plpgsql_error_funcname;

extern PLpgSQL_function *plpgsql_curr_compile;
extern MemoryContext plpgsql_compile_tmp_cxt;

extern PLpgSQL_plugin **plpgsql_plugin_ptr;

/**********************************************************************
 * 函数声明
 **********************************************************************/

/*
 * pl_comp.c中的函数
 */
extern PLpgSQL_function *plpgsql_compile(FunctionCallInfo fcinfo,
										 bool forValidator);
extern PLpgSQL_function *plpgsql_compile_inline(char *proc_source);
extern void plpgsql_parser_setup(struct ParseState *pstate,
								 PLpgSQL_expr *expr);
extern bool plpgsql_parse_word(char *word1, const char *yytxt, bool lookup,
							   PLwdatum *wdatum, PLword *word);
extern bool plpgsql_parse_dblword(char *word1, char *word2,
								  PLwdatum *wdatum, PLcword *cword);
extern bool plpgsql_parse_tripword(char *word1, char *word2, char *word3,
								   PLwdatum *wdatum, PLcword *cword);
extern PLpgSQL_type *plpgsql_parse_wordtype(char *ident);
extern PLpgSQL_type *plpgsql_parse_cwordtype(List *idents);
extern PLpgSQL_type *plpgsql_parse_wordrowtype(char *ident);
extern PLpgSQL_type *plpgsql_parse_cwordrowtype(List *idents);
extern PLpgSQL_type *plpgsql_build_datatype(Oid typeOid, int32 typmod,
											Oid collation,
											TypeName *origtypname);
extern PLpgSQL_variable *plpgsql_build_variable(const char *refname, int lineno,
												PLpgSQL_type *dtype,
												bool add2namespace);
extern PLpgSQL_rec *plpgsql_build_record(const char *refname, int lineno,
										 PLpgSQL_type *dtype, Oid rectypeid,
										 bool add2namespace);
extern PLpgSQL_recfield *plpgsql_build_recfield(PLpgSQL_rec *rec,
												const char *fldname);
extern int	plpgsql_recognize_err_condition(const char *condname,
											bool allow_sqlstate);
extern PLpgSQL_condition *plpgsql_parse_err_condition(char *condname);
extern void plpgsql_adddatum(PLpgSQL_datum *newdatum);
extern int	plpgsql_add_initdatums(int **varnos);
extern void plpgsql_HashTableInit(void);

/*
 * pl_handler.c中的函数
 */
extern void _PG_init(void);

/*
 * pl_exec.c中的函数
 */
extern Datum plpgsql_exec_function(PLpgSQL_function *func,
								   FunctionCallInfo fcinfo,
								   EState *simple_eval_estate,
								   ResourceOwner simple_eval_resowner,
								   ResourceOwner procedure_resowner,
								   bool atomic);
extern HeapTuple plpgsql_exec_trigger(PLpgSQL_function *func,
									  TriggerData *trigdata);
extern void plpgsql_exec_event_trigger(PLpgSQL_function *func,
									   EventTriggerData *trigdata);
extern void plpgsql_xact_cb(XactEvent event, void *arg);
extern void plpgsql_subxact_cb(SubXactEvent event, SubTransactionId mySubid,
							   SubTransactionId parentSubid, void *arg);
extern Oid	plpgsql_exec_get_datum_type(PLpgSQL_execstate *estate,
										PLpgSQL_datum *datum);
extern void plpgsql_exec_get_datum_type_info(PLpgSQL_execstate *estate,
											 PLpgSQL_datum *datum,
											 Oid *typeId, int32 *typMod,
											 Oid *collation);

/*
 * pl_funcs.c中用于命名空间处理的函数
 */
extern void plpgsql_ns_init(void);
extern void plpgsql_ns_push(const char *label,
							PLpgSQL_label_type label_type);
extern void plpgsql_ns_pop(void);
extern PLpgSQL_nsitem *plpgsql_ns_top(void);
extern void plpgsql_ns_additem(PLpgSQL_nsitem_type itemtype, int itemno, const char *name);
extern PLpgSQL_nsitem *plpgsql_ns_lookup(PLpgSQL_nsitem *ns_cur, bool localmode,
										 const char *name1, const char *name2,
										 const char *name3, int *names_used);
extern PLpgSQL_nsitem *plpgsql_ns_lookup_label(PLpgSQL_nsitem *ns_cur,
											   const char *name);
extern PLpgSQL_nsitem *plpgsql_ns_find_nearest_loop(PLpgSQL_nsitem *ns_cur);

/*
 * pl_funcs.c中的其他函数
 */
extern const char *plpgsql_stmt_typename(PLpgSQL_stmt *stmt);
extern const char *plpgsql_getdiag_kindname(PLpgSQL_getdiag_kind kind);
extern void plpgsql_free_function_memory(PLpgSQL_function *func);
extern void plpgsql_dumptree(PLpgSQL_function *func);

/*
 * pl_scanner.c中的扫描器函数
 */
extern int	plpgsql_base_yylex(void);
extern int	plpgsql_yylex(void);
extern int	plpgsql_token_length(void);
extern void plpgsql_push_back_token(int token);
extern bool plpgsql_token_is_unreserved_keyword(int token);
extern void plpgsql_append_source_text(StringInfo buf,
									   int startlocation, int endlocation);
extern int	plpgsql_peek(void);
extern void plpgsql_peek2(int *tok1_p, int *tok2_p, int *tok1_loc,
						  int *tok2_loc);
extern int	plpgsql_scanner_errposition(int location);
extern void plpgsql_yyerror(const char *message) pg_attribute_noreturn();
extern int	plpgsql_location_to_lineno(int location);
extern int	plpgsql_latest_lineno(void);
extern void plpgsql_scanner_init(const char *str);
extern void plpgsql_scanner_finish(void);

/*
 * gram.y中的外部
 */
extern int	plpgsql_yyparse(void);

#endif							/* PLPGSQL_H */
